package com.atgugui.gulimall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atgugui.gulimall.search.config.GulimallElasticsearchConfig;
import com.atgugui.gulimall.search.constant.EsConstant;
import com.atgugui.gulimall.search.service.MallSearchService;
import com.atgugui.gulimall.search.vo.SearchParamVo;
import com.atgugui.gulimall.search.vo.SearchResultVo;
import com.atguigu.common.to.es.SkuEsModel;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.ParsedAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Resource
    RestHighLevelClient client;

    //
    @Override
    public SearchResultVo search(SearchParamVo paramVo) {

        //组合查询条件
        SearchRequest request = buildSearchRequest(paramVo);

        SearchResultVo resultVo = null;
        try {
            SearchResponse response = client.search(request, GulimallElasticsearchConfig.COMMON_OPTIONS);
            //解析查询结果并封装
            resultVo = buildSearchResult(response, paramVo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return resultVo;
    }
    /**
     * 构建检索结果
     * @param response
     * @param paramVo
     * @return
     */
    private SearchResultVo buildSearchResult(SearchResponse response, SearchParamVo paramVo) {
        SearchResultVo resultVo = new SearchResultVo();
        SearchHits responseHits = response.getHits();

        //计算页数
        long total = responseHits.getTotalHits().value;
        resultVo.setTotal(total);
        resultVo.setTotalPages((total%EsConstant.PRODUCT_PAGESIZE==0?(int)total/EsConstant.PRODUCT_PAGESIZE:(int)total/EsConstant.PRODUCT_PAGESIZE+1));
        resultVo.setPageNum(paramVo.getPageNum());
        //查询产品结果构建
        List<SkuEsModel> esModels = new ArrayList<>();
        if(responseHits.getHits() != null && responseHits.getHits().length > 0){
            SearchHit[] hits = responseHits.getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
                if(!StringUtils.isEmpty(paramVo.getKeyword())){
                    Map<String, HighlightField> highlightField = hit.getHighlightFields();
                    esModel.setSkuTitle(highlightField.get("skuTitle").getFragments()[0].toString());
                }
                esModels.add(esModel);
            }
        }
        resultVo.setProducts(esModels);
        Aggregations aggregations = response.getAggregations();
        //构建属性信息
        List<SearchResultVo.AttrVo> attrVoList = new ArrayList<>();
        ParsedNested attrs_agg = aggregations.get("attrs_agg");
        ParsedLongTerms attr_id_agg = attrs_agg.getAggregations().get("attr_id_agg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SearchResultVo.AttrVo attrVo = new SearchResultVo.AttrVo();
            long attrId = bucket.getKeyAsNumber().longValue();
            ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");
            String attrName = attr_name_agg.getBuckets().get(0).getKeyAsString();
            ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");
            List<String> attrValues = attr_value_agg.getBuckets().stream().map(item -> {
                return ((Terms.Bucket)item).getKeyAsString();
            }).collect(Collectors.toList());
            attrVo.setAttrId(attrId);
            attrVo.setAttrName(attrName);
            attrVo.setAttrValue(attrValues);
            attrVoList.add(attrVo);
        }
        resultVo.setAttrs(attrVoList);
        //构建品牌信息
        List<SearchResultVo.BrandVo> brandVoList = new ArrayList<>();
        ParsedLongTerms brand_agg = aggregations.get("brand_agg");
        List<? extends Terms.Bucket> buckets = brand_agg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            SearchResultVo.BrandVo brandVo = new SearchResultVo.BrandVo();
            ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
            String brandImg = brand_img_agg.getBuckets().get(0).getKeyAsString();
            ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
            String brandName = brand_name_agg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
            brandVo.setBrandImg(brandImg);
            brandVo.setBrandName(brandName);
            brandVoList.add(brandVo);
        }
        resultVo.setBrands(brandVoList);
        //构建种类信息
        List<SearchResultVo.CatalogVo> catalogVoList = new ArrayList<>();
        ParsedLongTerms catalog_agg = aggregations.get("catalog_agg");
        for (Terms.Bucket bucket : catalog_agg.getBuckets()) {
            SearchResultVo.CatalogVo catalogVo = new SearchResultVo.CatalogVo();
            long catalogId = bucket.getKeyAsNumber().longValue();
            String catalog_name_agg = ((ParsedStringTerms) bucket.getAggregations().get("catalog_name_agg")).getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogId(catalogId);
            catalogVo.setCatalogName(catalog_name_agg);
            catalogVoList.add(catalogVo);
        }
        resultVo.setCatalogs(catalogVoList);
        return resultVo;
    }
    /**
     * 准备检索请求
     * #模糊匹配，过滤（属性，分类，品牌，价格区间，库存），排序，分页，高亮，聚合分析
     * @param paramVo
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParamVo paramVo) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //构建组合查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //1、模糊匹配
        if (!StringUtils.isEmpty(paramVo.getKeyword())){
            MatchQueryBuilder skuTitle = QueryBuilders.matchQuery("skuTitle", paramVo.getKeyword());
            boolQuery.must(skuTitle);
        }
        //2、过滤
        //2.1、分类过滤
        if(paramVo.getCatalog3Id() != null){
            boolQuery.filter(QueryBuilders.termQuery("catalogId", paramVo.getCatalog3Id()));
        }
        //2.2、是否有库存过滤
        if(paramVo.getHasStock() != null){
            boolQuery.filter(QueryBuilders.termQuery("hasStock", paramVo.getHasStock() == 1));
        }
        //2.3、品牌过滤
        if(paramVo.getBrandId() != null && paramVo.getBrandId().size() > 0){
            boolQuery.filter(QueryBuilders.termsQuery("brandId", paramVo.getBrandId()));
        }
        //2.4、价格区间过滤
        if(!StringUtils.isEmpty(paramVo.getSkuPrice())){
            String[] s = paramVo.getSkuPrice().split("_");
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            skuPrice.gte(s[0]);
            skuPrice.lte(s[1]);
            boolQuery.filter(skuPrice);
        }
        //2.5、属性过滤
        List<String> attrs = paramVo.getAttrs();
        if(attrs != null && attrs.size() > 0){
            for (String attr : attrs) {
                BoolQueryBuilder attrsQuery = QueryBuilders.boolQuery();
                String[] split = attr.split("_");
                //每一个都应该生成一个nested查询
                attrsQuery.filter(QueryBuilders.termQuery("attrs.attrId", split[0]));
                attrsQuery.filter(QueryBuilders.termsQuery("attrs.attrValue", split[1].split(":")));
                NestedQueryBuilder nested = QueryBuilders.nestedQuery("attrs", attrsQuery, ScoreMode.None);
                boolQuery.filter(nested);
            }
        }
        sourceBuilder.query(boolQuery);
        //3、排序
        if(!StringUtils.isEmpty(paramVo.getSort())){
            String[] sort = paramVo.getSort().split("_");
            sourceBuilder.sort(sort[0], sort[1].toUpperCase().equals(SortOrder.DESC)?SortOrder.DESC:SortOrder.ASC);
        }
        //4、分页
        sourceBuilder.from((paramVo.getPageNum()-1) * EsConstant.PRODUCT_PAGESIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
        //5、高亮
        if(!StringUtils.isEmpty(paramVo.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        //6、聚合分析
        //6.1、分类聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
        brand_agg.size(50).field("brandId");
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").size(1).field("brandName"));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").size(1).field("brandImg"));
        sourceBuilder.aggregation(brand_agg);
        //6.2、品牌聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg");
        catalog_agg.size(50).field("catalogId");
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").size(1).field("catalogName"));
        sourceBuilder.aggregation(catalog_agg);
        //6.3、属性聚合
        NestedAggregationBuilder attrs_agg = AggregationBuilders.nested("attrs_agg","attrs");
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").size(100).field("attrs.attrId");
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").size(1).field("attrs.attrName"));
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").size(1).field("attrs.attrValue"));
        attrs_agg.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(attrs_agg);

        String s = sourceBuilder.toString();
        System.out.println("组合的查询数据》"+s);

        SearchRequest request = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
        return request;
    }

}
